package com.windea.study.kotlin.jvm.crypt.sections

import java.io.*
import java.security.*
import java.security.spec.*
import javax.crypto.*

object RsaCrypt {
	private const val maxEncryptSize = 117 //每次最大加密长度
	private const val maxDecryptSize = 128 //每次最大解密长度
	
	fun getKeyPair(): Pair<PublicKey, PrivateKey> {
		val generator = KeyPairGenerator.getInstance("RSA")
		val keyPair = generator.genKeyPair()
		val publicKey = keyPair.public
		val privateKey = keyPair.private
		return publicKey to privateKey
	}
	
	fun encryptByPrivateKey(input: ByteArray, privateKey: ByteArray): ByteArray {
		return encrypt(input, getPrivateKey(privateKey))
	}
	
	fun encryptByPublicKey(input: ByteArray, publicKey: ByteArray): ByteArray {
		return encrypt(input, getPublicKey(publicKey))
	}
	
	fun decryptByPrivateKey(input: ByteArray, privateKey: ByteArray): ByteArray {
		return encrypt(input, getPrivateKey(privateKey))
	}
	
	fun decryptByPublicKey(input: ByteArray, publicKey: ByteArray): ByteArray {
		return encrypt(input, getPublicKey(publicKey))
	}
	
	private fun getPublicKey(key: ByteArray): PublicKey = KeyFactory.getInstance("RSA").generatePublic(X509EncodedKeySpec(key))
	
	private fun getPrivateKey(key: ByteArray): PrivateKey = KeyFactory.getInstance("RSA").generatePrivate(X509EncodedKeySpec(key))
	
	private fun encrypt(input: ByteArray, key: Key): ByteArray {
		val cipher = Cipher.getInstance("RSA")
		cipher.init(Cipher.ENCRYPT_MODE, key)
		//分段加密
		return sectionOperation(input, cipher, maxEncryptSize)
	}
	
	private fun decrypt(input: ByteArray, key: Key): ByteArray {
		val cipher = Cipher.getInstance("RSA")
		cipher.init(Cipher.DECRYPT_MODE, key)
		//分段加密
		return sectionOperation(input, cipher, maxDecryptSize)
	}
	
	private fun sectionOperation(input: ByteArray, cipher: Cipher, maxSize: Int): ByteArray {
		var temp: ByteArray
		var offset = 0 //当前偏移位置
		val outPutStream = ByteArrayOutputStream()
		outPutStream.use {
			while(input.size - offset > 0) {
				if(input.size - offset >= maxSize) {
					temp = cipher.doFinal(input, offset, maxSize)
					offset += maxSize
				} else {
					//加密最后一块
					temp = cipher.doFinal(input, offset, input.size - offset)
					offset = input.size
				}
				outPutStream.write(temp)
			}
		}
		return outPutStream.toByteArray()
	}
}
